﻿using DotNetty.Buffers;
using DotNetty.Codecs;
using DotNetty.Handlers.Timeout;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using Raspberry.Printer.Sockets.Common;
using Raspberry.Printer.Sockets.Encoder;
using Raspberry.Printer.Sockets.HandlerAdapter;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace Raspberry.Printer.Sockets.Client
{
    /// <summary>
    /// 基础的Socket服务器，只提供IByteBuffer的支持
    /// </summary>
    /// <typeparam name="TContext"></typeparam>
    public abstract class SocketClient : IDisposable
    {
        public SocketClient()
        {
            Setup();
        }


        private void Setup()
        {
            //声明一个服务端Bootstrap，每个Netty服务端程序，都由ServerBootstrap控制，
            bootstrap = new Bootstrap();
            // 主工作线程组，设置为1个线程
            bossGroup = new MultithreadEventLoopGroup();
        }

        /// <summary>
        /// 管道处理程序
        /// </summary>
        /// <param name="channel"></param>
        private void MasterHandler(ISocketChannel channel)
        {
            //工作线程连接器 是设置了一个管道，服务端主线程所有接收到的信息都会通过这个管道一层层往下传输
            //同时所有出栈的消息 也要这个管道的所有处理器进行一步步处理
            IChannelPipeline pipeline = channel.Pipeline;

            pipeline.AddLast(new MessageEncoder());
            pipeline.AddLast(new MessageDecoder());

            //默认编码器
            //pipeline.AddLast("Encode", new LengthFieldPrepender(4));
            //默认解码器
            //pipeline.AddLast("Decode", new LengthFieldBasedFrameDecoder(ushort.MaxValue, 0, 4, 0, 4));
            // IdleStateHandler 心跳
            //服务端为读IDLE
            pipeline.AddLast(new IdleStateHandler(0, 10, 0));//第一个参数为读，第二个为写，第三个为读写全部
            //业务handler ，这里是实际处理业务的Handler
            pipeline.AddLast(new DefaultClientHandler(this));
        }

        /// <summary>
        /// 启动服务
        /// </summary>
        /// <param name="inetPort">监听端口</param>
        /// <returns></returns>
        public async Task Connect(String lpcstrnetHost, int inetPort)
        {
            await Connect(IPAddress.Parse(lpcstrnetHost), inetPort);
        }

        /// <summary>
        /// 启动服务
        /// </summary>
        /// <param name="inetHost">监听本地地址</param>
        /// <param name="inetPort">监听端口</param>
        /// <returns></returns>
        public async Task Connect(IPAddress inetHost, int inetPort)
        {
            try
            {
                // 设置主和工作线程组
                bootstrap.Group(bossGroup);
                // 设置通道模式为TcpSocket
                bootstrap.Channel<TcpSocketChannel>();
                //
                bootstrap.Option(ChannelOption.TcpNodelay, true);
                //设置管道处理程序
                bootstrap.Handler(new ActionChannelInitializer<ISocketChannel>(MasterHandler));
                // bootstrap绑定到指定端口的行为 就是服务端启动服务，同样的Serverbootstrap可以bind到多个端口
                ConnectChannel = await bootstrap.ConnectAsync(new IPEndPoint(inetHost, inetPort));
            }
            catch (Exception ex)
            {
                this.OnErrord(null, ex.Message, ex);
                //释放工作组线程
                await Shutdown();
            }
        }





        /// <summary>
        /// 停止，中断所有工作
        /// </summary>
        /// <returns></returns>
        public async Task Shutdown()
        {
            if (ConnectChannel != null)
            {
                await ConnectChannel.CloseAsync();
                ConnectChannel = null;
            }
            if (bossGroup != null)
            {
                await Task.WhenAll
                (
                    bossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1))
                );
                bossGroup = null;
            }
            if (bootstrap != null)
            {
                bootstrap = null;
            }
        }



        public void Dispose()
        {
            Shutdown().Wait();
        }


        #region MyRegion

        /// <summary>
        /// 接收协议数据包
        /// 使用此事件应禁止重写 OnReceive事件
        /// </summary>
       //、、 public abstract void OnReceiveData(UserToken<TContext> context, IByteBuffer buffer);

        public abstract void OnErrord(UserToken context, string Message, Exception ex);

        public abstract void OnConnect(UserToken context);

        public abstract void OnClosed(UserToken context);


        public abstract void OnReceivePackage(UserToken context, Byte [] message);

        #endregion






        private MultithreadEventLoopGroup bossGroup { get; set; }
        private Bootstrap bootstrap { get; set; }
        private IChannel ConnectChannel { get; set; }
    }
}
